/**
 * @file factorylib.c
 * @brief Factory for library (sdlib: Select Dynamic Library).
 *
 * Sorgente creato per permettere l'utilizzo del caricamento delle librerie dinamiche
 *  in maniera centralizzata e seguendo il design pattern factory
 * Responsabilità:
 *  - tenere in memoria tutte le librerie caricate per non caricarne duplicati inutilmente;
 *  - gestire il caricamento, supportando il client nella richiesta delle librerie e nella chiamata alle funzioni
 *  - individuare le informazioni sulle librerie
 *
 *
 * @date Feb 25, 2012
 * @author Francesco Corvino
 */
#include "factorylib.h"

/**
 * Restituisce il numero di librerie totali
 * Uso privato
 */
static int num_elements = 0;

/**
 * Indica quanto spazio si è allocato per le successive allocazioni
 * Uso privato
 */
static int num_allocated = 0;

/**
 * Locazione dei puntatori che descrivono le librerie caricate in memoria
 * Uso privato
 */
DescrLib ** libBuffer = NULL;

static void (* functionlog)(char*) = NULL;

/**
 * @description Setter della funzione di logging esterna
 */
void setLoggingFunc(void (*funlog)(char*)){
	functionlog = funlog;
}

/**
 * Riferimento nelle funzioni locali ai logger
 * @param args argomento stringa da passare alle funzioni
 * @description funzione proxy verso le librerie di logging:
 * se si vuole abilitare le funzioni di logging richiamare la
 * funzione <code>setLoggingFunc</code> passandogli il corretto puntatore
 *
 * Funzione usata a uso privato
 */
void localLog(char *args){
	if(functionlog != NULL)
		(*functionlog)(args);
}

/**
 * Funzione privata, da non richiamare esternamente
 * @description utilizzata per allocare dinamicamente lo spazio per le librerie
 */
int __privAddLib (DescrLib * item){
	if(num_elements == num_allocated) { // Are more refs required?
		// Feel free to change the initial number of refs
		// and the rate at which refs are allocated.
		if (num_allocated == 0) num_allocated = 3; // Start off with 3 refs
		else {		// Double the number of refs allocated
			num_allocated *= 2;
			localLog("Aumentata la dimensione del buffer");
		}

		// Make the reallocation transactional by using a temporary variable first
		void *_tmp = realloc(libBuffer, (num_allocated * sizeof(DescrLib*)));
		if (!_tmp) {
			// If the reallocation didn't go so well, inform the user and bail out
			localLog("ERROR: Couldn't realloc memory!\n");
			return(-1);
		}
		libBuffer = (DescrLib**)_tmp; // Things are looking good so far
	}
	libBuffer[num_elements] = item;
	num_elements++;
	localLog("Inserimento in memoria della libreria");
	return num_elements;
}

/**
 * @description Inizializza i dati della factory
 * Inserire qui ulteriore codice per l'inizializzazione
 */
void initialize(){
	// inizializza l'ultimo indice a 0
	num_elements=0;
	num_allocated=0;
}

/**
 * @description Svuota la memoria di ogni libreria caricata e della memoria allocata
 */
void terminate(){
	int i;
	// chiusura delle librerie
	for(i=0;i<num_elements;i++){
		closeLibrary(libBuffer[i]->libpointer);
	}
	// deallocazione array
	free(libBuffer);
}

/**
 * @description Rimuove una particolare libreria
 * @param idLib indice della libreria da eliminare, se non corrisponde a nessuna libreria
 * la funzione non fa nulla.
 */
void deleteLib(int idLib){
	// la ricerca della libreria nell'array corrisponde a ricavare il puntatore idLib poichè non c'è differenza
	// tra idLib e indice nell'array, è necessario modificare il codice se si cambia la truttura dati
	if(idLib>num_elements) return; // id non valido
	DescrLib *p = libBuffer[idLib];
	// successivamente provvede a controllare se è già stata eliminata
	if(p==NULL) return; // libreria già eliminata
	// altrimenti la elimina
	closeLibrary(p->libpointer);
	libBuffer[idLib] = NULL;
	// se è l'ultima libera spazio
	if(idLib==num_elements) num_elements--;
}

/**
 * @description Prova a caricare una libreria
 */
int loadLib(DescrLib * lib){
	int assignedId = num_elements + 1; // prepara id dove verrà posizionata
	// procedura per controllare che la libreria non sia già presente o abbia particolari privilegi
	if(lib->type!=0) {// si da' la possibilità di caricare in ogni caso la libreria
		int i;
		for(i=0;i<num_elements;i++){ // controlla ogni libreria
			DescrLib *p = libBuffer[i];
			if(p==NULL) {assignedId = i; continue;} // buco nell'array
			if(strcmp(p->name, lib->name)==0){ // confronto nomi
				localLog("si era tentato di creare la libreria ma c'è stato un'errore");
				if(p->libpointer == NULL) {
					// riprova
					p->libpointer = loadLibrary(lib->path);
				}
				return 0; // libreria già presente
			}
		}
	}
	localLog("libreria non presente, provo a caricarla");
	lib->libpointer = loadLibrary(lib->path);
	lib->idLib = assignedId;
	if(lib->libpointer == NULL) {
		localLog("errore nella creazione");
		return -1;
	}
	localLog("aggiorna i dati inserendo la libreria in memoria e aggiornando il contatore");
	__privAddLib(lib);
	return 0;
}

/**
 * @description getter for loaded libraries
 */
DescrLib * getLibrary(int idLib){
	if(idLib>num_elements) return NULL; // id non valido
	return libBuffer[idLib];
}

/**
 * @description Valida le librerie specificate per una libreria
 */
int validateLib(int idLib){
	//TODOL implementazione
	return 0;
}
